#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <errno.h>

#define PORT 8080
#define MAX_CLIENTS 10
#define BUFFER_SIZE 1024

int main() {
	    int master_socket, client_sockets[MAX_CLIENTS];
	        struct sockaddr_in address;
		    int addrlen = sizeof(address);
		        char buffer[BUFFER_SIZE];
			    
			    for (int i = 0; i < MAX_CLIENTS; i++) {
				            client_sockets[i] = 0;
					        }
			        if ((master_socket = socket(AF_INET, SOCK_STREAM, 0)) == 0) {
					        perror("socket failed");
						        exit(EXIT_FAILURE);
							    }
				    
				    address.sin_family = AF_INET;
				        address.sin_addr.s_addr = INADDR_ANY;
					    address.sin_port = htons(PORT);
					        
					        if (bind(master_socket, (struct sockaddr *)&address, sizeof(address)) < 0) {
							        perror("bind failed");
								        exit(EXIT_FAILURE);
									    }
						    
						    if (listen(master_socket, 3) < 0) {
							            perror("listen");
								            exit(EXIT_FAILURE);
									        }
						        
						        printf("Multiplexing server started on port %d\n", PORT);
							    
							    while (1) {
								            fd_set readfds;
									            int max_sd, activity;
										            
										            FD_ZERO(&readfds);
											            
											            FD_SET(master_socket, &readfds);
												            max_sd = master_socket;
													            
													            for (int i = 0; i < MAX_CLIENTS; i++) {
															                int sd = client_sockets[i];
																	            if (sd > 0) {
																			                    FD_SET(sd, &readfds);
																					                }
																		                if (sd > max_sd) {
																					                max_sd = sd;
																							            }
																				        }
														            
														            activity = select(max_sd + 1, &readfds, NULL, NULL, NULL);
															            if ((activity < 0) && (errno != EINTR)) {
																	                perror("select error");
																			        }
																            
																            if (FD_ISSET(master_socket, &readfds)) {
																		                int new_socket;
																				            if ((new_socket = accept(master_socket, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) {
																						                    perror("accept");
																								                    exit(EXIT_FAILURE);
																										                }
																					                
																					                printf("New connection, socket fd is %d, IP: %s, port: %d\n",
																									                  new_socket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));
																							            
																							            for (int i = 0; i < MAX_CLIENTS; i++) {
																									                    if (client_sockets[i] == 0) {
																												                        client_sockets[i] = new_socket;
																															                    printf("Adding to list of sockets as %d\n", i);
																																	                        break;
																																				                }
																											                }
																								            }
																	            
																	            for (int i = 0; i < MAX_CLIENTS; i++) {
																			                int sd = client_sockets[i];
																					            if (FD_ISSET(sd, &readfds)) {
																							                    int valread = read(sd, buffer, BUFFER_SIZE);
																									                    if (valread == 0) {
																												                      
																												                        getpeername(sd, (struct sockaddr*)&address, (socklen_t*)&addrlen);
																															                    printf("Host disconnected, IP %s, port %d\n",
																																			                              inet_ntoa(address.sin_addr), ntohs(address.sin_port));
																																	                        close(sd);
																																				                    client_sockets[i] = 0;
																																						                    } else {
																																									                        
																																									                        buffer[valread] = '\0';
																																												                    printf("Received from client %d: %s\n", sd, buffer);
																																														                        send(sd, buffer, strlen(buffer), 0);
																																																	                }
																											                }
																						            }
																		        }
							        
							        return 0;
}
